import org.vu.contest.ContestSubmission;
import org.vu.contest.ContestEvaluation;

import java.util.Random;
import java.util.Properties;
import java.util.Arrays;
import java.util.Comparator;

class Crossover {
    int geneStart, geneEnd; // the indexes of genes the operator works on, operationStart inclusive, operationEnd exclusive
    Crossover(int geneStart, int geneEnd) {
        this.geneStart = geneStart;
        this.geneEnd = geneEnd;
    }
    public double[][] cross(double[] individual1, double[] individual2) {
        throw new UnsupportedOperationException();
    }
}

class ParentSelection {
    public int[] selectParents(Individual[] population, int populationSize) {return null;}
    public void initSelection(Individual[] population, int populationSize) {}
}

interface ISurvivalSelection {
    public void select(Individual[] population, int populationSize);
}

class Mutation {
    final boolean evaluatesOffspring; // if the operator evaluates the offspring  
    Mutation(boolean evaluatesOffspring) {
        this.evaluatesOffspring = evaluatesOffspring;
    }
    public void initGeneration() { };
    // If the operator evaluates the offspring,
    // it Must evaluate the new individual and return false, it there were no more evaluations
    public boolean mutate(Individual individual) {
        throw new UnsupportedOperationException();
    }
}

interface IInitialisaion {
    public void initialise(Individual[] population, int populationSize);
}

class InUniformRandom implements IInitialisaion {
    private ContestEvaluation evaluation_;
    private Random rnd_;
    public InUniformRandom(Random rnd, ContestEvaluation evaluation) {
        evaluation_ = evaluation;
        rnd_ = rnd;
    }
    private double[] genomebuff = new double[10];
    public void initialise(Individual[] population, int populationSize) {
        for (int i = 0; i < populationSize; ++i) {
            population[i] = new Individual();
            for (int j = 0; j < 10; ++j) {
                population[i].genome[j] = rnd_.nextDouble() * 10 - 5;
                genomebuff[j] = population[i].genome[j] ;
            }
            population[i].fitness = (Double) evaluation_.evaluate(genomebuff);
        }
    }
}

class PSUniformRandom extends ParentSelection {
    private Random rnd_;
	private boolean allowCloning;
    public PSUniformRandom(Random rnd, boolean allowCloning) {
        this.allowCloning = allowCloning;
        rnd_ = rnd;
    }
    public int[] selectParents(Individual[] population, int populationSize) {
        int[] parents = new int[2];
        parents[0] = rnd_.nextInt(populationSize);
        parents[1] = rnd_.nextInt(populationSize);
        if (!allowCloning) {
            while(parents[0] == parents[1])
                parents[1] = rnd_.nextInt(populationSize);
        }
        return parents;
    }
}

class PSRouletteWheel extends ParentSelection {
    // WARNING: this is slow, do not use on the first test function
    private Random rnd_;
    double sumFitness;
    double minFitness;
	private boolean allowCloning;
    public PSRouletteWheel(Random rnd, boolean allowCloning) {
        rnd_ = rnd;
        this.allowCloning = allowCloning;
    }
    public void initSelection(Individual[] population, int populationSize) {
        
        sumFitness = population[0].fitness;
        minFitness = population[0].fitness;
        for(int i = 1; i < populationSize; ++i) {
            sumFitness += population[i].fitness;
            if(minFitness > population[i].fitness)
                minFitness = population[i].fitness;
        }
        sumFitness -= populationSize * minFitness;
    }
    public int[] selectParents(Individual[] population, int populationSize) {
        int[] parents = new int[2];
        
        double draw = rnd_.nextDouble() * sumFitness;
        parents[0] = 0;
        while (draw > (population[parents[0]].fitness - minFitness)) {
            draw = draw - (population[parents[0]].fitness - minFitness);
            parents[0]++;
        }
        
        draw = rnd_.nextDouble() * sumFitness;
        parents[1] = 0;
        while (draw > (population[parents[1]].fitness - minFitness)) {
            draw = draw - (population[parents[1]].fitness - minFitness);
            parents[1]++;
        }
        if (!allowCloning) {
            while(parents[1] == parents[0]) {
                draw = rnd_.nextDouble() * sumFitness;
                parents[1] = 0;
                while (draw > (population[parents[1]].fitness - minFitness)) {
                    draw = draw - (population[parents[1]].fitness - minFitness);
                    parents[1]++;
                }
            }
        }
        return parents;
    }
}

class PSRankBased extends ParentSelection {
	private Random rnd_;
	private double s, c1, c2; 
	private boolean allowCloning;
	// 1.0 < s <= 2.0, if s is 0.0 then it implements exponential
	public PSRankBased(Random rnd, double s, boolean allowCloning) {
		rnd_ = rnd;
		this.s = s;
		this.allowCloning = allowCloning;
	}
	public void initSelection(Individual[] population, int populationSize) {
		Arrays.sort(population, 0, populationSize, new Comparator<Individual>() {
            @Override
            public int compare(Individual o1, Individual o2) {
                return ((Double) o2.fitness).compareTo(o1.fitness);
            }
        });
		if (s == 0.0) {
			c1 = populationSize;
			for (int i = 0; i<populationSize; ++i) {
				c1 -= Math.exp(-i);
			}
			c1 = 1.0/c1;
			c2 = Math.exp(-1);
		} else {
			c1 = (2.0-s)/populationSize;
			c2 = 2.0*(s-1)/((populationSize-1)*populationSize);
		}
	}
	public int[] selectParents(Individual[] population, int populationSize) {
		int[] parents = new int[2];
		double draw = rnd_.nextDouble();
		parents[0] = 0;
		if (s == 0.0) {
			double prob = c1;
			while (draw > c1 - prob) {
				draw -= c1 - prob;
				prob *= c2;
				parents[0]++;
			}
		} else {
			while (draw > (c1 + parents[0]*c2)) {
				draw -= c1 + parents[0]*c2;
				parents[0]++;
			}
		}
		
		draw = rnd_.nextDouble();
		parents[1] = 0;
		if (s == 0.0) {
			double prob = c1;
			while (draw > c1 - prob) {
				draw -= c1 - prob;
				prob *= c2;
				parents[1]++;
			}
		} else {
			while (draw > (c1 + parents[1]*c2)) {
				draw -= c1 + parents[1]*c2;
				parents[1]++;
			}
		}
		if (!allowCloning) {
			while(parents[1] == parents[0]) {
				draw = rnd_.nextDouble();
				parents[1] = 0;
				if (s == 0.0) {
					double prob = c1;
					while (draw > c1 - prob) {
						draw -= c1 - prob;
						prob *= c2;
						parents[1]++;
					}
				} else {
					while (draw > (c1 + parents[1]*c2)) {
						draw -= c1 + parents[1]*c2;
						parents[1]++;
					}
				}
			}
		}
		parents[0] = populationSize - parents[0] - 1;
		parents[1] = populationSize - parents[1] - 1;
		return parents;
	}
}

class SSAnnihilation implements ISurvivalSelection {
	public SSAnnihilation() {}
	public void select(Individual[] population, int populationSize) {
		for(int i = populationSize; i < population.length; i++) {
			population[i - populationSize].copy(population[i]);
		}
	}
}

class SSRankBased implements ISurvivalSelection {
    private Random rnd_;
    private double s, c1, c2; 
    // 1.0 < s <= 2.0, if s is 0.0 then it implements exponential
    public SSRankBased(Random rnd, double s) {
        rnd_ = rnd;
        this.s = s;
    }
    public void select(Individual[] population, int populationSize) {
        Arrays.sort(population, 0, population.length, new Comparator<Individual>() {
            @Override
            public int compare(Individual o1, Individual o2) {
                return ((Double) o2.fitness).compareTo(o1.fitness);
            }
        });
        if (s == 0.0) {
            c1 = population.length;
            for (int i = 0; i<population.length; ++i) {
                c1 -= Math.exp(-i);
            }
            c1 = 1.0/c1;
            c2 = Math.exp(-1);
        } else {
            c1 = (2.0-s)/population.length;
            c2 = 2.0*(s-1)/((population.length-1)*population.length);
        }
        
        int selected;
        double draw;
        for(int i = populationSize; i < population.length; i++) {
            draw = rnd_.nextDouble();
            selected = 0;
            if (s == 0.0) {
                double prob = c1;
                while (draw > c1 - prob) {
                    draw -= c1 - prob;
                    prob *= c2;
                    selected++;
                }
            } else {
                while (draw > (c1 + selected*c2)) {
                    draw -= c1 + selected*c2;
                    selected++;
                }
            }
            population[i - populationSize].copy(population[selected]);
        }
        
        
    }
}

class SSSimpleSort implements ISurvivalSelection {
	public SSSimpleSort() {}
	public void select(Individual[] population, int populationSize) {
		Arrays.sort(population, new Comparator<Individual>() {
            @Override
            public int compare(Individual o1, Individual o2) {
                return ((Double) o2.fitness).compareTo(o1.fitness);
            }
        });
	}
}

class NPointCrossover extends Crossover {
	private int N;
	private Random rnd_;
	public NPointCrossover(Random random, int geneStart, int geneEnd, int N) {
	    super(geneStart, geneEnd);
		this.N = N;
		rnd_ = random;
	}
	public double[][] cross(double[] parent1, double[] parent2) {
		if (N > geneEnd - geneStart - 1)
			return null;
		
		int[] Xpoints = new int[N];
		int numxpoints = 0;
		int draw;
		int i;
		while(numxpoints < N) {
			draw = 1 + rnd_.nextInt(geneEnd - geneStart-1);
			i = 0;
			while (i < numxpoints && Xpoints[i] != draw)
				i++;
			if (i == numxpoints) {
				Xpoints[numxpoints] = draw;
				numxpoints++;
			}
		}
		
		Arrays.sort(Xpoints);
		
		double[][] children = new double[2][geneEnd - geneStart];
		
		boolean flip = false;
		numxpoints = 0;
		for (i = 0; i < geneEnd - geneStart; ++i) {
			if (numxpoints < N && Xpoints[numxpoints] == i) {
				numxpoints++;
				flip = !flip;
			}
			if(flip) {
				children[0][i] = parent2[geneStart + i];
				children[1][i] = parent1[geneStart + i];
			} else {
				children[0][i] = parent1[geneStart + i];
				children[1][i] = parent2[geneStart + i];
			}
		}
		return children;
	}
}

class IstvanNFlipCrossover extends Crossover {
	private int N;
	private Random rnd_;
	public IstvanNFlipCrossover(Random random, int geneStart, int geneEnd, int N) {
	    super(geneStart, geneEnd);
		this.N = N;
		rnd_ = random;
	}
	public double[][] cross(double[] parent1, double[] parent2) {
		if (N > geneEnd - geneStart)
			return null;
		
		int[] Xpoints = new int[N];
		int numxpoints = 0;
		int draw;
		int i;
		while(numxpoints < N) {
			draw = rnd_.nextInt(geneEnd - geneStart);
			i = 0;
			while (i < numxpoints && Xpoints[i] != draw)
				i++;
			if (i == numxpoints) {
				Xpoints[numxpoints] = draw;
				numxpoints++;
			}
		}
		
		Arrays.sort(Xpoints);
		
		double[][] children = new double[2][geneEnd - geneStart];
		
		numxpoints = 0;
		for (i = 0; i < geneEnd - geneStart; ++i) {
			if (numxpoints < N && Xpoints[numxpoints] == i) {
				numxpoints++;
				children[0][i] = parent2[geneStart+i];
				children[1][i] = parent1[geneStart+i];
			} else {
				children[0][i] = parent1[geneStart+i];
				children[1][i] = parent2[geneStart+i];
			}
		}
		return children;
	}
}

class RandomFlipCrossover extends Crossover {
    private Random rnd_;
    public RandomFlipCrossover(Random random, int geneStart, int geneEnd) {
        super(geneStart, geneEnd);
        rnd_ = random;
    }
    public double[][] cross(double[] parent1, double[] parent2) {
        double[][] children = new double[1][geneEnd - geneStart];
        for (int i = 0; i < geneEnd - geneStart; ++i) {
            if (rnd_.nextBoolean())
                children[0][i] = parent1[geneStart + i];
            else
                children[0][i] = parent2[geneStart + i];
        }
        return children;
    }
}

class AveragerCrossover extends Crossover {
    public AveragerCrossover(int geneStart, int geneEnd) {
        super(geneStart, geneEnd);
    }
    public double[][] cross(double[] parent1, double[] parent2) {
        double[][] children = new double[1][geneEnd - geneStart];
        for (int i = 0; i < geneEnd - geneStart; ++i) {
            children[0][i] = (parent1[geneStart + i] + parent2[geneStart + i]) / 2.0;
        }
        return children;
    }
}

class CompoundCrossover extends Crossover {
    private Crossover crossover1;
    private Crossover crossover2;
    public CompoundCrossover(Crossover crossover1, Crossover crossover2) {
        super(crossover1.geneStart, crossover2.geneEnd);
        this.crossover1 = crossover1;
        this.crossover2 = crossover2;
    }
    public double[][] cross(double[] parent1, double[] parent2) {
        double[][] children1 = crossover1.cross(parent1, parent2);
        double[][] children2 = crossover2.cross(parent1, parent2);
        int numOffspring;
        if (children2.length < children1.length)
            numOffspring = children2.length;
        else
            numOffspring = children1.length;
        double[][] children = new double[numOffspring][geneEnd-geneStart];
        for (int i = 0; i < crossover1.geneEnd - geneStart; ++i) {
            for (int j = 0; j < numOffspring; ++j) {
                children[j][i] = children1[j][i];
            }
        }
        for(int i = 0; i < geneEnd - crossover2.geneStart; ++i) {
            for (int j = 0; j < numOffspring; ++j) {
                children[j][i+crossover1.geneEnd] = children2[j][i];
            }
        }
        return children;
    }
}

class UniformMutation extends Mutation {
	private double flip_prob;
	private Random rnd_;
	public UniformMutation(Random random, double prob) {
	    super(false);
		flip_prob = prob;
		rnd_ = random;
	}
	public boolean mutate(Individual individual) {
		for(int i = 0; i < individual.genome.length; ++i){
			if (flip_prob > rnd_.nextDouble()) {
				individual.genome[i] = rnd_.nextDouble()*10.0 - 5.0;
			}
		}
		return true;
	}
}

class GaussianMutation extends Mutation {
	private double flip_prob;
	private Random rnd_;
	private double mean;
	private double mean_dic;
	private ContestEvaluation evaluation_;
	public GaussianMutation(Random random, ContestEvaluation evaluation, double prob, double mean) {
		this(random, evaluation, prob, mean, 1.0);
	}
	public GaussianMutation(Random random, ContestEvaluation evaluation, double prob, double mean, double mean_dic) {
	    super(false);
		evaluation_ = evaluation;
		flip_prob = prob;
		rnd_ = random;
		this.mean = mean;
		this.mean_dic = mean_dic;
	}
	public boolean mutate(Individual individual) {
		for(int i = 0; i < individual.genome.length; ++i){
			if (flip_prob > rnd_.nextDouble()) {
				individual.genome[i] = rnd_.nextGaussian()*mean + individual.genome[i];
				if (individual.genome[i] > 5)
					individual.genome[i] = 5;
				else if (individual.genome[i] < -5)
					individual.genome[i] = -5;
			}
		}
		return true;
	}
	public void initGeneration() {
		mean *= mean_dic;
	}
}

class GaussianMutationWithGlobalControl extends Mutation {
    private double flip_prob;
    private Random rnd_;
    private ContestEvaluation evaluation_;
    public GaussianMutationWithGlobalControl(Random random, ContestEvaluation evaluation, double prob) {
        super(true);
        evaluation_ = evaluation;
        flip_prob = prob;
        rnd_ = random;
    }
    private double[] g = new double[10];
    
    private double calGamma(double fitness) {
        double coef;
        double epsilon = 0.00000000001;
        if (10.0 - fitness > 1.0)
            coef = Math.sqrt((10.0 - fitness));
        else {
            coef = (10.0 - fitness)/0.4;
//            coef = Math.pow(coef, 1.1);
        }
        
        if (coef < epsilon)
            coef = epsilon;
        return coef;
    }
    
    public boolean mutate(Individual individual) {
        
        for (int j = 0; j < 10; ++j) {
            g[j] = individual.genome[j];
        }
        Double fitness = (Double) evaluation_.evaluate(g);
        if (fitness == null)
            return false;
        individual.fitness = fitness.doubleValue();
        double gamma = calGamma(individual.fitness);

        for(int i = 0; i < 10; ++i){
            if (flip_prob > rnd_.nextDouble()) {
                individual.genome[i] = rnd_.nextGaussian()*gamma + individual.genome[i];
                if (individual.genome[i] > 5)
                    individual.genome[i] = 5;
                else if (individual.genome[i] < -5)
                    individual.genome[i] = -5;
            }
        }
        
        for (int j = 0; j < 10; ++j) {
            g[j] = individual.genome[j];
        }
        fitness = (Double) evaluation_.evaluate(g);
        if (fitness == null)
            return false;
        individual.fitness = fitness.doubleValue();
//        individual.genome[10] = Math.sqrt(10.0 - individual.fitness);
        return true;
    }
}

class UncorrelatedWithNSigmas extends Mutation {
    private double tau, taucommon, epsilon;
    private Random rnd_;
    public UncorrelatedWithNSigmas(Random random, double taucommon, double tau, double epsilon) {
        super(false);
        this.rnd_ = random;
        this.taucommon = taucommon;
        this.tau = tau; 
        this.epsilon = epsilon;
    }
    public boolean mutate(Individual individual) {
        double n = Math.exp(taucommon * rnd_.nextGaussian());
        for(int j = 10; j < 20; ++j) {
            individual.genome[j] = individual.genome[j] * Math.exp(tau * rnd_.nextGaussian()) * n;
            if(individual.genome[j] < epsilon)
                individual.genome[j] = epsilon;
        }
        for(int j = 0; j < 10; ++j){
            individual.genome[j] += individual.genome[j+10] * rnd_.nextGaussian();
            if (individual.genome[j] > 5)
                individual.genome[j] = 5;
            else if (individual.genome[j] < -5)
                individual.genome[j] = -5;
        }
        return true;
    }
}

class Individual {
	public double[] genome;
	public double fitness; 
	public int successfulMutations = 0;
	public Individual() {
		fitness = 100;
		genome = new double[20];
		for (int i = 10; i < genome.length; ++i) {
			genome[i] = 1;
		}
	}
	public void copy(Individual other) {
		genome = other.genome.clone();
		fitness = other.fitness;
	}
}

public class player19 implements ContestSubmission
{
	private Random rnd_;
	private ContestEvaluation evaluation_;
	private int evaluations_limit_;
	private Mutation mutation_;
	private Crossover crossover_;
	private ParentSelection parentSelection_;
	private ISurvivalSelection survivalSelection_;
	private IInitialisaion initialisation_;
	private boolean isMultimodal;
	private boolean hasStructure;
	private boolean isSeparable;
	
	public static void main(String[] args) {
		int runs = 10;
		double result = 0.0;
		for (int i = 0; i < runs; ++i) {
		    ContestEvaluation eval = new SphereEvaluation();
	        ContestSubmission sub = new player19();
	        sub.setSeed(System.currentTimeMillis());
	        sub.setEvaluation(eval);
	        sub.run();
		    result += eval.getFinalResult();
		}
		System.out.println("Result: "+result/runs);
	}
	
	public player19()
	{
		rnd_ = new Random();
	}

	public void setSeed(long seed)
	{
		// Set seed of algorithms random process
		rnd_.setSeed(seed);
	}

	public void setEvaluation(ContestEvaluation evaluation)
	{
		// Set evaluation problem used in the run
		evaluation_ = evaluation;

		// Get evaluation properties
		Properties props = evaluation.getProperties();
		evaluations_limit_ = Integer.parseInt(props.getProperty("Evaluations"));
		isMultimodal = Boolean.parseBoolean(props.getProperty("Multimodal"));
		hasStructure = Boolean.parseBoolean(props.getProperty("GlobalStructure"));
		isSeparable = Boolean.parseBoolean(props.getProperty("Separable"));
		
		if(isMultimodal){
			// Do sth
		}else{
			// Do sth else
		}
	}
	
	private void method(int num) {
		switch (num) {
		case 1:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSUniformRandom(rnd_, false);
			crossover_ = new NPointCrossover(rnd_, 0, 10, 3);
			mutation_ = new UniformMutation(rnd_, 0.1);
			survivalSelection_ = new SSSimpleSort();
			break;
		case 2:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSUniformRandom(rnd_, false);
			crossover_ = new NPointCrossover(rnd_, 0, 10, 3);
			mutation_ = new UniformMutation(rnd_, 0.7);
			survivalSelection_ = new SSAnnihilation();
			break;
		case 3:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSUniformRandom(rnd_, false);
			crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 3);
			mutation_ = new UniformMutation(rnd_, 0.1);
			survivalSelection_ = new SSSimpleSort();
			break;
		case 4:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSUniformRandom(rnd_, false);
			crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 3);
			mutation_ = new GaussianMutation(rnd_, evaluation_, 0.09, 2);
			survivalSelection_ = new SSSimpleSort();
			break;
		case 5:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSRouletteWheel(rnd_, false);
			crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 2);
			mutation_ = new GaussianMutation(rnd_, evaluation_, 0.3, 1);
			survivalSelection_ = new SSAnnihilation();
			break;
		case 6:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSRouletteWheel(rnd_, false);
			crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 2);
			mutation_ = new GaussianMutation(rnd_, evaluation_, 0.29, 0.9);
			survivalSelection_ = new SSAnnihilation();
			break;
		case 7:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSRankBased(rnd_, 1.9, true);
			crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 4);
			mutation_ = new GaussianMutation(rnd_, evaluation_, 0.1, 0.4);
			survivalSelection_ = new SSAnnihilation();
			break;
		case 8:
			initialisation_ = new InUniformRandom(rnd_, evaluation_);
			parentSelection_ = new PSRankBased(rnd_, 1.9, true);
			crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 3);
			mutation_ = new GaussianMutation(rnd_, evaluation_, 0.7, 1, 0.91);
			survivalSelection_ = new SSAnnihilation();
			break;
		case 9:
		    initialisation_ = new InUniformRandom(rnd_, evaluation_);
		    parentSelection_ = new PSUniformRandom(rnd_, true);
		    crossover_ = new CompoundCrossover(new RandomFlipCrossover(rnd_, 0, 10),
		                                       new AveragerCrossover(10, 20));
		    mutation_ = new UncorrelatedWithNSigmas(rnd_,  1.0 / Math.sqrt(2*10), 1.0 / Math.sqrt(2 * Math.sqrt(10)), 0.000000001);
		    survivalSelection_ = new SSSimpleSort();
		    break;
		case 10:
            initialisation_ = new InUniformRandom(rnd_, evaluation_);
            parentSelection_ = new PSUniformRandom(rnd_, true);
            crossover_ = new CompoundCrossover(new IstvanNFlipCrossover(rnd_, 0, 10, 4),
                                               new AveragerCrossover(10, 20));
            mutation_ = new GaussianMutationWithGlobalControl(rnd_, evaluation_, 8.0);
            survivalSelection_ = new SSSimpleSort();
            break;
		case 11:
		    initialisation_ = new InUniformRandom(rnd_, evaluation_);
            parentSelection_ = new PSUniformRandom(rnd_, true);
            crossover_ = new CompoundCrossover(new RandomFlipCrossover(rnd_, 0, 10),
                                               new AveragerCrossover(10, 20));
            mutation_ = new GaussianMutationWithGlobalControl(rnd_, evaluation_, 8.0);
            survivalSelection_ = new SSSimpleSort();
            break;
		case 12:
            initialisation_ = new InUniformRandom(rnd_, evaluation_);
            parentSelection_ = new PSUniformRandom(rnd_, true);
//            crossover_ = null;
//            crossover_ = new AveragerCrossover(0, 10);
            crossover_ = new IstvanNFlipCrossover(rnd_, 0, 10, 5);
            mutation_ = new GaussianMutationWithGlobalControl(rnd_, evaluation_, 1.0);
            survivalSelection_ = new SSSimpleSort();
            break;
		default:
			break;
		}
		
	}
	
    public void run()
    {
        if (isMultimodal) {
//          method(7);
            method(11);
            
            int populationSize = 15;
            int numOffsprings = 60;
            
            Individual[] population = new Individual[populationSize+numOffsprings];
            
            initialisation_ = new InUniformRandom(rnd_, evaluation_);
            initialisation_.initialise(population, populationSize);
            
            for (int i = populationSize; i < populationSize + numOffsprings; i++) {
                population[i] = new Individual();
            }
            boolean running = true;
            double tau = 1.0 / Math.sqrt(2 * Math.sqrt(10)) * 1.01;  
            double taucommon = 1.0 / Math.sqrt(2*10) * 1.01;
            double epsilon  = 0.000000001;
            double epsilon2 = 0.0000000001;
            int parent1, parent2;
            double[] genomebuff = new double[10];
            double bestfitness;
            while (running) {
                bestfitness = -9999999;
                for(int i = 0; i<populationSize; ++i) {

                    if (bestfitness < population[i].fitness)
                        bestfitness = population[i].fitness;
                }

                for (int i = populationSize; i < populationSize + numOffsprings; i += 1) {
                    parent1 = rnd_.nextInt(populationSize);
                    parent2 = rnd_.nextInt(populationSize);
                    for (int j = 0; j < 10; ++j) {
                        double ratio;
                        double switchlimit = 8.9;
                        if (population[parent1].fitness == 0.0 && population[parent2].fitness == 0.0)
                            ratio = 0.5;
                        else if (population[parent1].fitness > switchlimit && population[parent2].fitness > switchlimit)
                            ratio = (population[parent1].fitness - switchlimit) 
                            / (population[parent1].fitness + population[parent2].fitness - 18.0);
                        else
                            ratio = population[parent1].fitness 
                                    / (population[parent1].fitness + population[parent2].fitness);
                        double ratiomin = 0.4;
                        if (ratio < ratiomin)
                            ratio = ratiomin;
                        else if (ratio > 1.0 - ratiomin)
                            ratio = 1.0 - ratiomin;
                        population[i].genome[j] = population[parent1].genome[j] * ratio
                                                 + population[parent2].genome[j] * (1.0-ratio);
                    }
                    for(int j = 10; j < 20; ++j) {
                        population[i].genome[j] = (population[parent1].genome[j] + population[parent2].genome[j]) / 2.0;
                    }
                }
                
                for (int i = populationSize; i < populationSize+ numOffsprings; ++i) {
                    // GAMMA  MUTATION
                    double n = Math.exp(taucommon * rnd_.nextGaussian());
                    for(int j = 10; j < 20; ++j) {
                        population[i].genome[j] = population[i].genome[j] * Math.exp(tau * rnd_.nextGaussian()) * n;
                        if(population[i].genome[j] < epsilon)
                            population[i].genome[j] = epsilon;
                    }
                    
                    // GENOME  MUTATION
                    for(int j = 0; j < 10; ++j){
                        population[i].genome[j] += population[i].genome[j+10] * rnd_.nextGaussian();
                        if (population[i].genome[j] > 5)
                            population[i].genome[j] = 5;
                        else if (population[i].genome[j] < -5)
                            population[i].genome[j] = -5;
                    }

                    // FITTNESS  EVALUATION
                    for (int j = 0; j < 10; ++j) {
                        genomebuff[j] = population[i].genome[j];
                    }
                    Double fitness = (Double) evaluation_.evaluate(genomebuff);
                    if (fitness == null || fitness.doubleValue() == 10.0) {
                        running = false;
                        break;
                    }
                    population[i].fitness = fitness.doubleValue();
                    if (population[i].fitness < 9.0) {
                        double sum = 0.0;
                        for (int j = 10; j < 20; ++j) {
                            sum += population[i].genome[j] * population[i].genome[j];
                        }
                        sum = Math.sqrt(sum);
                        double coef;
                        if (9.0  > population[i].fitness) {
                            coef = (10.0 - population[i].fitness) / 6;
                            coef = Math.pow(coef, 1.1);
                        } else {
                            coef = (10.0 - population[i].fitness);
                            coef = Math.pow(coef, 0.3);
                        }
                        
                        if (coef < epsilon2)
                            coef = epsilon2;
                        
                        coef /= sum;
                        for (int j = 10; j < 20; ++j) {
                            population[i].genome[j] *= coef;
                        }
                    }
                }
                Arrays.sort(population, new Comparator<Individual>() {
                    @Override
                    public int compare(Individual o1, Individual o2) {
                        return ((Double) o2.fitness).compareTo(o1.fitness);
                    }
                });
            }
            
            int remain = 0;
            while (evaluation_.evaluate(genomebuff) != null)
                remain++;
            System.out.println("Remaining evals: " + String.valueOf(remain));
            if (true) return;
            
        } else {
            method(12);

            int populationSize = 15;
            int numOffsprings = 60;
            
            Individual[] population = new Individual[populationSize+numOffsprings];
            
            initialisation_ = new InUniformRandom(rnd_, evaluation_);
            initialisation_.initialise(population, populationSize);
            
            for (int i = populationSize; i < populationSize + numOffsprings; i++) {
                population[i] = new Individual();
            }
            boolean running = true;
            double tau = 1.0 / Math.sqrt(2 * Math.sqrt(10)) * 1.01;  
            double taucommon = 1.0 / Math.sqrt(2*10) * 1.01;
            double epsilon  = 0.000000001;
            double epsilon2 = 0.0000000001;
            int parent1, parent2;
            
            double[] genomebuff = new double[10];
            double bestfitness;
            while (running) {
                bestfitness = -9999999;

                for(int i = 0; i<populationSize; ++i) {

                    if (bestfitness < population[i].fitness)
                        bestfitness = population[i].fitness;
                }

                for (int i = populationSize; i < populationSize + numOffsprings; i += 1) {
                    parent1 = rnd_.nextInt(populationSize);
                    parent2 = rnd_.nextInt(populationSize);
                    for (int j = 0; j < 10; ++j) {
                        parent1 = rnd_.nextInt(populationSize);
                        parent2 = rnd_.nextInt(populationSize);

                        double ratio;
                        double switchlimit = 8.9;
                        if (population[parent1].fitness <= 0.0 && population[parent2].fitness <= 0.0)
                            ratio = 0.5;
                        else if (population[parent1].fitness > switchlimit && population[parent2].fitness > switchlimit)
                            ratio = (population[parent1].fitness - switchlimit) 
                            / (population[parent1].fitness + population[parent2].fitness - 18.0);
                        else
                            ratio = population[parent1].fitness 
                                    / (population[parent1].fitness + population[parent2].fitness);
                        double ratiomin = 0.3;
                        if (ratio < ratiomin)
                            ratio = ratiomin;
                        else if (ratio > 1.0 - ratiomin)
                            ratio = 1.0 - ratiomin;
                        population[i].genome[j] = population[parent1].genome[j] * ratio
                                                 + population[parent2].genome[j] * (1.0-ratio);
                    }
                    for(int j = 10; j < 20; ++j) {
                        population[i].genome[j] = (population[parent1].genome[j] + population[parent2].genome[j]) / 2.0;
                    }
                }
                
                for (int i = populationSize; i < populationSize+ numOffsprings; ++i) {
                    // GAMMA  MUTATION
                    double n = Math.exp(taucommon * rnd_.nextGaussian());
                    for(int j = 10; j < 20; ++j) {
                        population[i].genome[j] = population[i].genome[j] * Math.exp(tau * rnd_.nextGaussian()) * n;
                        if(population[i].genome[j] < epsilon)
                            population[i].genome[j] = epsilon;
                    }
                    
                    // GENOME  MUTATION
                    for(int j = 0; j < 10; ++j){
                        population[i].genome[j] += population[i].genome[j+10] * rnd_.nextGaussian();
                        if (population[i].genome[j] > 5)
                            population[i].genome[j] = 5;
                        else if (population[i].genome[j] < -5)
                            population[i].genome[j] = -5;
                    }

                    // FITTNESS  EVALUATION
                    for (int j = 0; j < 10; ++j) {
                        genomebuff[j] = population[i].genome[j];
                    }
                    Double fitness = (Double) evaluation_.evaluate(genomebuff);
                    if (fitness == null || fitness.doubleValue() == 10.0) {
                        running = false;
                        break;
                    }
                    population[i].fitness = fitness.doubleValue();
                    if (population[i].fitness < 8.0) {
                        double sum = 0.0;
                        for (int j = 10; j < 20; ++j) {
                            sum += population[i].genome[j] * population[i].genome[j];
                        }
                        sum = Math.sqrt(sum);
                        double coef;
                        if (9.0  > population[i].fitness) {
                            coef = (10.0 - population[i].fitness) / 4;
                        } else {
                            coef = (10.0 - population[i].fitness);
                            coef = Math.pow(coef, 0.3);
                        }                     
                        if (coef < epsilon2)
                            coef = epsilon2;                       
                        coef /= sum;
                        for (int j = 10; j < 20; ++j) {
                            population[i].genome[j] *= coef;
                        }
                    }
                }
                Arrays.sort(population, new Comparator<Individual>() {
                    @Override
                    public int compare(Individual o1, Individual o2) {
                        return ((Double) o2.fitness).compareTo(o1.fitness);
                    }
                });
            }
         
            int remain = 0;
            while (evaluation_.evaluate(genomebuff) != null)
                remain++;
            System.out.println("Remaining evals: " + String.valueOf(remain));
            return;
            
		}
		int populationSize = 100;
		int numOffsprings = 107;
		
		Individual[] population = new Individual[populationSize+numOffsprings];
		
		initialisation_.initialise(population, populationSize);
		
		for (int i = populationSize; i < populationSize + numOffsprings; i++) {
			population[i] = new Individual();
		}
		double bestfitness;
		boolean running = true;
		double[] g = new double[10];
		while (running) {
			bestfitness = -9999999;
			for(int i = 0; i<populationSize; ++i) {
				if (bestfitness < population[i].fitness)
					bestfitness = population[i].fitness;
			}
			parentSelection_.initSelection(population, populationSize);
			for(int i = 0; i < numOffsprings;) {
				int[] parents = parentSelection_.selectParents(population, populationSize);
				
				if (crossover_ != null) {
    				double[][] crossed = crossover_.cross(population[parents[0]].genome,
    													  population[parents[1]].genome);
    				for (int j = 0; j < crossed.length; ++j) {
        				population[populationSize + i + j].genome  = crossed[j];
        				population[populationSize + i + j].fitness = population[parents[0]].fitness;
    				}
    				i += crossed.length;
				} else {
				    population[populationSize + i    ].genome  = population[parents[0]].genome.clone();
				    population[populationSize + i    ].fitness = population[parents[0]].fitness;
				    population[populationSize + i + 1].genome  = population[parents[1]].genome.clone();
				    population[populationSize + i + 1].fitness = population[parents[1]].fitness;
				    i += 2;
				}
			}
			mutation_.initGeneration();
			for (int i = populationSize; i < populationSize + numOffsprings; ++i) {
			    if (mutation_.evaluatesOffspring) {
    				if (!mutation_.mutate(population[i])) {
    					running = false;
    					break;
    				}
			    } else {
			        mutation_.mutate(population[i]);
			        for (int j = 0; j < 10; ++j) {
			            g[j] = population[i].genome[j];
			        }
			        Double fitness = (Double) evaluation_.evaluate(g);
			        if (fitness == null) {
			            running = false;
                        break;
			        }
			        population[i].fitness = fitness.doubleValue();
			    }
			}
			survivalSelection_.select(population, populationSize);
		}
	}
}
